package com.liuguang.common.utils;
 
 
import org.bouncycastle.jce.provider.BouncyCastleProvider;
 
import javax.crypto.Cipher;
import java.math.BigInteger;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
 
public class EncryForRSAUtil {
 
    private static String digits = "0123456789abcdef";
 
    static {
        Security.addProvider(new BouncyCastleProvider());
    }
 
    /**
     * 随机生成密钥对 未验证
     * @return HashMap<String , Object>
     * @throws NoSuchAlgorithmException
     */
    public static HashMap<String , Object> getKeys() throws NoSuchAlgorithmException {
        HashMap<String , Object> map = new HashMap<>();
        // KeyPairGenerator类用于生成公钥和私钥对，基于RSA算法生成对象
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
        // 初始化密钥对生成器，密钥大小为96-1024位
        keyPairGen.initialize(1024);
        // 生成一个密钥对，保存在keyPair中
        KeyPair keyPair = keyPairGen.generateKeyPair();
        //得到公钥
        RSAPublicKey publicKey = (RSAPublicKey)keyPair.getPublic();
        //得到私钥
        RSAPrivateKey privateKey = (RSAPrivateKey)keyPair.getPrivate();
        map.put("publicKey" , publicKey);
        map.put("privateKey" , privateKey);
        return map;
    }
 
    /**
     * 使用模和指数生成RSA公钥
     * 注意：【此代码用了默认补位方式，为RSA/None/PKCS1Padding，不同JDK默认的补位方式可能不同，如Android默认是RSA
     * 	 * /None/NoPadding】
     * @param argM 模    N
     * @param argE 指数  E
     * @return
     */
    public static RSAPublicKey getPublicKey(String argM , String argE){
        try {
            BigInteger bigM = new BigInteger(argM);
            BigInteger bigE = new BigInteger(argE);
            KeyFactory factory = KeyFactory.getInstance("RSA", "BC");
            RSAPublicKeySpec spec = new RSAPublicKeySpec(bigM , bigE);
            return (RSAPublicKey)factory.generatePublic(spec);
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }
 
    /**
     * 使用模和指数生成RSA私钥 
     * 注意：【此代码用了默认补位方式，为RSA/None/PKCS1Padding，不同JDK默认的补位方式可能不同，如Android默认是RSA
     * 	 * /None/NoPadding】
     * @param argM 模     N
     * @param argE 指数   E
     * @return
     */
    public static RSAPrivateKey getPrivateKey(String argM , String argE){
        try {
            BigInteger bigM = new BigInteger(argM);
            BigInteger bigE = new BigInteger(argE);
            KeyFactory factory = KeyFactory.getInstance("RSA", "BC");
            RSAPrivateKeySpec spec = new RSAPrivateKeySpec(bigM , bigE);
            return (RSAPrivateKey) factory.generatePrivate(spec);
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }
 
    /**
     * 公钥加密
     * @param data
     * @param publicKey
     * @return
     * @throws Exception
     */
    public static String encryptByPublicKey(String data , RSAPublicKey publicKey) throws Exception {
 
        byte[] bInput = data.getBytes("UTF8");
        Cipher cipher = Cipher.getInstance("RSA/NONE/PKCS1Padding", "BC");
 
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        //模 长
        int CellLength = publicKey.getModulus().bitLength() / 8;
        int MaxLength = CellLength - 11;
        int GroupLength = (int)Math.ceil((double)bInput.length * 1.0D / (double)MaxLength);
        byte[] result = new byte[GroupLength * CellLength];
 
        for(int i = 0; i < GroupLength; ++i) {
            int len = MaxLength < bInput.length - MaxLength * i ? MaxLength : bInput.length - MaxLength * i;
            byte[] inByte = new byte[len];
            System.arraycopy(bInput, MaxLength * i, inByte, 0, len);
            byte[] temp = cipher.doFinal(inByte);
            System.arraycopy(temp, 0, result, i * CellLength, CellLength);
        }
        return StringUtil.toHex(result , digits);
    }
 
    /**
     * 公钥加密
     * @param data 明文数据
     * @param argM 模
     * @param argE 指数
     * @return
     * @throws Exception
     */
    public static String encryptByPublicKey(String data , String argM , String argE) throws Exception {
        RSAPublicKey publicKey = getPublicKey(argM , argE);
        return encryptByPublicKey(data , publicKey);
    }
 
 
 
 
    /**
     * 私钥解密  未验证
     * @param data
     * @param privateKey
     * @return
     * @throws Exception
     */
    public static String decryByPrivateKey(String data, RSAPrivateKey privateKey) throws Exception {
        byte[] bInput = StringUtil.hexStr2Bytes(data);
        Cipher cipher = Cipher.getInstance("RSA/NONE/PKCS1Padding", "BC");
 
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        //模 长
        int CellLength = privateKey.getModulus().bitLength() / 8;
        int MaxLength = CellLength - 11;
        int GroupLength = (int)Math.ceil((double)bInput.length * 1.0D / (double)CellLength);
        List<byte[]> plainList = new ArrayList();
        int len = 0;
 
        for(int i = 0; i < GroupLength; ++i) {
            byte[] inByte = new byte[CellLength];
            System.arraycopy(bInput, CellLength * i, inByte, 0, CellLength);
            byte[] temp = cipher.doFinal(inByte);
            plainList.add(temp);
            len += temp.length;
        }
        byte[] plainText = new byte[len];
        for(int i = 0; i < GroupLength; ++i) {
            System.arraycopy(plainList.get(i), 0, plainText, i * MaxLength, ((byte[])plainList.get(i)).length);
        }
        return byteToString(plainText);
 
    }
 
    /**
     * 私钥解密   未验证
     * @param data 密文
     * @param argM 模
     * @param argE 指数
     * @return
     * @throws Exception
     */
    public static String decryByPrivateKey(String data , String argM , String argE) throws Exception {
        RSAPrivateKey privateKey = getPrivateKey(argM , argE);
        return decryByPrivateKey(data , privateKey);
    }
 
    public static String byteToString(byte[] bytes) {
        return byteToString(bytes, bytes.length);
    }
 
    public static String byteToString(byte[] bytes, int length) {
        char[] chars = new char[length];
 
        for(int i = 0; i != chars.length; ++i) {
            chars[i] = (char)(bytes[i] & 255);
        }
 
        return new String(chars);
    }
 
}

